package net.realsee.framework.plc;

import HslCommunication.Core.Types.OperateResult;
import HslCommunication.Core.Types.OperateResultExOne;
import HslCommunication.Profinet.Siemens.SiemensPLCS;
import HslCommunication.Profinet.Siemens.SiemensS7Net;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.realsee.common.enums.wcs.plc.PlcReadWriteErrEnum;
import net.realsee.domain.wcs.device.bo.PlcReadWriteBo;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 针对西门子S7协议设备的操作
 *
 * @author 孙正华
 * @since 2023/8/15 16:58
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class S7ProtocolReadWriteImpl implements IPlcReadWrite {

    /**
     * 设备编号和驱动的对应集合
     */
    public Map<String, SiemensS7Net> deviceSiemensS7NetMap = new ConcurrentHashMap<>();

    @Override
    public PlcOperateResult read(PlcReadWriteBo readWriteBo) {
        SiemensS7Net siemensS7Net = getSiemensS7Net(readWriteBo);
        if (ObjectUtil.isNull(siemensS7Net)
            || ObjectUtil.isNull(siemensS7Net.GetPipeSocket()) || siemensS7Net.GetPipeSocket().IsConnectitonError()) {
            return new PlcOperateResult(PlcReadWriteErrEnum.DEVICE_OFFLINE);
        }
        String tagAddr = readWriteBo.getAddr();
        String tagType = readWriteBo.getType();
        // 地址支持I,Q,M,DB,AI,AQ，举例：I100,Q0,M100,DB1.0,AI0,AQ16
        if (!StrUtil.startWithAnyIgnoreCase(tagAddr, "I", "Q", "M", "DB", "AI", "AQ")) {
            return new PlcOperateResult(PlcReadWriteErrEnum.TAG_NOT_EXIST);
        }

        PlcOperateResult result = new PlcOperateResult();
        if (StrUtil.equalsIgnoreCase(tagType, "string")) {
            // 读100位 TODO
            OperateResultExOne<String> readString = siemensS7Net.ReadString(tagAddr, (short) 100);
            if (readString.IsSuccess) {
                result.setSuccess(true);
                result.setContent(readString.Content.trim());
            } else {
                result.setSuccess(false);
                result.setErrorCode(readString.ErrorCode);
                result.setMessage(readString.Message);
            }
        } else if (StrUtil.equalsIgnoreCase(tagType, "short")) {
            OperateResultExOne<Short> readShort = siemensS7Net.ReadInt16(tagAddr);
            if (readShort.IsSuccess) {
                result.setSuccess(true);
                result.setContent(readShort.Content.toString());
            } else {
                result.setSuccess(false);
                result.setErrorCode(readShort.ErrorCode);
                result.setMessage(readShort.Message);
            }
        } else if (StrUtil.equalsIgnoreCase(tagType, "float")) {
            OperateResultExOne<Float> readFloat = siemensS7Net.ReadFloat(tagAddr);
            if (readFloat.IsSuccess) {
                result.setSuccess(true);
                result.setContent(String.valueOf(new BigDecimal(readFloat.Content).intValue()));
            } else {
                result.setSuccess(false);
                result.setErrorCode(readFloat.ErrorCode);
                result.setMessage(readFloat.Message);
            }
        } else if (StrUtil.equalsIgnoreCase(tagType, "boolean")) {
            OperateResultExOne<Boolean> readBool = siemensS7Net.ReadBool(tagAddr);
            if (readBool.IsSuccess) {
                result.setSuccess(true);
                result.setContent(String.valueOf(readBool.Content));
            } else {
                result.setSuccess(false);
                result.setErrorCode(readBool.ErrorCode);
                result.setMessage(readBool.Message);
            }
        } else {
            result = new PlcOperateResult(PlcReadWriteErrEnum.TAG_TYPE_NOT_SUPPORT);
        }
        return result;
    }

    @Override
    public PlcOperateResult write(PlcReadWriteBo readWriteBo) {
        SiemensS7Net siemensS7Net = getSiemensS7Net(readWriteBo);
        if (ObjectUtil.isNull(siemensS7Net)) {
            return new PlcOperateResult(PlcReadWriteErrEnum.DEVICE_OFFLINE);
        }
        String tagAddr = readWriteBo.getAddr();
        String tagType = readWriteBo.getType();
        String toWriteValue = readWriteBo.getToWriteValue();
        // 地址支持I,Q,M,DB,AI,AQ，举例：I100,Q0,M100,DB1.0,AI0,AQ16
        if (!StrUtil.startWithAnyIgnoreCase(tagAddr, "I", "Q", "M", "DB", "AI", "AQ")) {
            return new PlcOperateResult(PlcReadWriteErrEnum.TAG_NOT_EXIST);
        }

        PlcOperateResult result = PlcOperateResult.builder().isSuccess(true).build();
        if (StrUtil.equalsIgnoreCase(tagType, "string")) {
            // 写100位 TODO
            OperateResult write = siemensS7Net.Write(tagAddr, toWriteValue, 100);
            if (!write.IsSuccess) {
                result = new PlcOperateResult(PlcReadWriteErrEnum.UNKNOWN);
            }
        } else if (StrUtil.equalsIgnoreCase(tagType, "short")) {
            OperateResult write = siemensS7Net.Write(tagAddr, Short.parseShort(toWriteValue));
            if (!write.IsSuccess) {
                result = new PlcOperateResult(PlcReadWriteErrEnum.UNKNOWN);
            }
        } else if (StrUtil.equalsIgnoreCase(tagType, "float")) {
            OperateResult write = siemensS7Net.Write(tagAddr, Float.parseFloat(toWriteValue));
            if (!write.IsSuccess) {
                result = new PlcOperateResult(PlcReadWriteErrEnum.UNKNOWN);
            }
        } else if (StrUtil.equalsIgnoreCase(tagType, "boolean")) {
            OperateResult write = siemensS7Net.Write(tagAddr, Boolean.parseBoolean(toWriteValue));
            if (!write.IsSuccess) {
                result = new PlcOperateResult(PlcReadWriteErrEnum.UNKNOWN);
            }
        } else {
            result = new PlcOperateResult(PlcReadWriteErrEnum.TAG_TYPE_NOT_SUPPORT);
        }
        return result;
    }

    @Override
    public PlcOperateResult monitor() {
        return null;
    }

    /**
     * 获取西门子的数据交互类，采用s7协议实现
     */
    public SiemensS7Net getSiemensS7Net(PlcReadWriteBo readWriteBo) {
        // 1.缓存中获取
        SiemensS7Net siemensS7Net = deviceSiemensS7NetMap.get(readWriteBo.getDevice());
        // 2.不存在/连接状态发生了异常，则创建交互类。
        if (ObjectUtil.isNull(siemensS7Net)
            || ObjectUtil.isNull(siemensS7Net.GetPipeSocket()) || siemensS7Net.GetPipeSocket().IsConnectitonError()) {
            siemensS7Net = createSiemensS7Net(readWriteBo);
        }
        return siemensS7Net;
    }

    /**
     * 创建西门子的数据交互类，采用s7协议实现
     */
    public SiemensS7Net createSiemensS7Net(PlcReadWriteBo readWriteBo) {
        // 1.销毁
        destroy(readWriteBo.getDevice());
        // 2.创建
        SiemensS7Net result = new SiemensS7Net(SiemensPLCS.S1200, readWriteBo.getCommAddr());
        // 3.判断连接状态是否正常
        if (ObjectUtil.isNotNull(result)) {
            result.SetPersistentConnection();
            deviceSiemensS7NetMap.put(readWriteBo.getDevice(), result);
            log.debug("设备{} IP{}创建交互类的类成功", readWriteBo.getDevice(), readWriteBo.getCommAddr());
        } else {
            log.error("设备{} IP{}创建交互类的类失败", readWriteBo.getDevice(), readWriteBo.getCommAddr());
        }
        return result;
    }

    /**
     * 销毁西门子的数据交互类
     */
    @Override
    public void destroy(String device) {
        SiemensS7Net siemensS7Net = deviceSiemensS7NetMap.get(device);
        if (ObjectUtil.isNotNull(siemensS7Net)) {
            // 从缓存中删除
            deviceSiemensS7NetMap.remove(device);
            // 手动断开与远程服务器的连接，如果当前是长连接模式，那么就会切换到短连接模式
            siemensS7Net.ConnectClose();
        }
    }
}
